﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.Models;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;
using MugenMvvmToolkit.ViewModels;
using MugenMvvmToolkitTest.TestInfrastructure;
using MugenMvvmToolkitTest.TestModels;
using Should;

namespace MugenMvvmToolkitTest.ViewModel
{
    public abstract class MultiViewModelTest : TestBase
    {
        #region Nested types

        public class CloseableViewModelItem : WorkspaceViewModel
        {
            #region Constructors

            /// <summary>
            ///     Initializes a new instance of the <see cref="CloseableViewModelItem" /> class.
            /// </summary>
            public CloseableViewModelItem()
            {
                Closing = true;
            }

            #endregion

            #region Properties

            public new bool Closing { get; set; }

            #endregion

            #region Overrides of WorkspaceViewModel

#if TASKSUPPORT
            /// <summary>
            ///     Occurs when view model closing.
            /// </summary>
            /// <returns>
            ///     If <c>true</c> - close, otherwise <c>false</c>.
            /// </returns>
            protected override Task<bool> OnClosing(object parameter)
            {
                if (Closing)
                    return MvvmUtils.TrueTaskResult;
                return MvvmUtils.FalseTaskResult;
            }
#else
            /// <summary>
            ///     Occurs on closing current view model.
            /// </summary>
            /// <returns>
            ///     If <c>true</c> - close, otherwise <c>false</c>.
            /// </returns>
            protected override IOperationResult<bool> OnClosing(object parameter)
            {
                if (Closing)
                    return MvvmUtils.TrueOperationResult;
                return MvvmUtils.FalseOperationResult;
            }
#endif
            #endregion
        }

        #endregion

        #region Test methods

        [TestMethod]
        public void ItemsSourceShouldNotBeNull()
        {
            var multiViewModel = GetMultiViewModel();
            multiViewModel.ItemsSource.ShouldNotBeNull();
        }

        [TestMethod]
        public void AddShouldAddsVmToItemsSource()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();

            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);
            multiViewModel.ItemsSource.Count.ShouldEqual(1);
        }

        [TestMethod]
        public void AddShouldNotThrowExceptionOnDuplicate()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.AddViewModel(viewModel);
        }

        [TestMethod]
        public void InsertShouldInsertsVmToItemsSourceInTheSpecifiedIndex()
        {
            var viewModel1 = GetViewModel<CloseableViewModelItem>();
            var viewModel0 = GetViewModel<CloseableViewModelItem>();

            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel1);
            multiViewModel.ItemsSource.IndexOf(viewModel1).ShouldEqual(0);

            multiViewModel.ItemsSource.Insert(0, viewModel0);
            multiViewModel.ItemsSource.IndexOf(viewModel0).ShouldEqual(0);
            multiViewModel.ItemsSource.IndexOf(viewModel1).ShouldEqual(1);
        }

        [TestMethod]
        public void RemoveMethodShouldCallCloseMethodInVmFalse()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            viewModel.Closing = false;
            multiViewModel.RemoveViewModel(viewModel).Result.ShouldBeFalse();
            multiViewModel.ItemsSource.ShouldContain(viewModel);
        }

        [TestMethod]
        public void RemoveMethodShouldCallCloseMethodInVmTrue()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            viewModel.Closing = true;
            multiViewModel.RemoveViewModel(viewModel).Result.ShouldBeTrue();
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
        }

        [TestMethod]
        public void RemoveMethodShouldDisposeVmIfSetToTrue()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.DisposeViewModelOnRemove = true;
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            viewModel.Closing = true;
            multiViewModel.RemoveViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeTrue();
        }

        [TestMethod]
        public void RemoveMethodShouldNotDisposeVmIfSetToFalse()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.DisposeViewModelOnRemove = false;
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            viewModel.Closing = true;
            multiViewModel.RemoveViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeFalse();
        }

        [TestMethod]
        public void ClearShouldDisposeVmIfSetToTrue()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            multiViewModel.Clear(true);
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeTrue();
        }


        [TestMethod]
        public void ClearShouldNotDisposeVmIfSetToFalse()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            multiViewModel.Clear(false);
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeFalse();
        }

        [TestMethod]
        public void WhenVmDisposedItShouldDisposeVmIfSetToTrue()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.DisposeViewModelsOnDispose = true;
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            multiViewModel.Dispose();
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeTrue();
        }

        [TestMethod]
        public void WhenVmDisposedItShouldNotDisposeVmIfSetToFalse()
        {
            var viewModel = GetViewModel<CloseableViewModelItem>();
            var multiViewModel = GetMultiViewModel();
            multiViewModel.DisposeViewModelsOnDispose = false;
            multiViewModel.AddViewModel(viewModel);
            multiViewModel.ItemsSource.ShouldContain(viewModel);

            multiViewModel.Dispose();
            multiViewModel.ItemsSource.ShouldNotContain(viewModel);
            viewModel.IsDisposed.ShouldBeFalse();
        }

        [TestMethod]
        public void WhenAddVmItShouldBeSelected()
        {
            var viewModel0 = GetViewModel<CloseableViewModelItem>();
            var viewModel1 = GetViewModel<CloseableViewModelItem>();

            var multiViewModel = GetMultiViewModel();
            multiViewModel.AddViewModel(viewModel0);
            multiViewModel.AddViewModel(viewModel1);

            multiViewModel.SelectedItem.ShouldEqual(viewModel1);
            viewModel1.IsSelected.ShouldBeTrue();
            viewModel0.IsSelected.ShouldBeFalse();

            multiViewModel.RemoveViewModel(viewModel1);
            multiViewModel.SelectedItem.ShouldEqual(viewModel0);
            viewModel1.IsSelected.ShouldBeFalse();
            viewModel0.IsSelected.ShouldBeTrue();
        }

        #endregion

        #region Methods

        private IMultiViewModel GetMultiViewModel()
        {
            var viewModel = GetMultiViewModelInternal();
            InitializeViewModel(viewModel, IocAdapter, GlobalSettings.DefaultActivationPolicy);
            return viewModel;
        }

        protected abstract IMultiViewModel GetMultiViewModelInternal();

        #endregion

    }
}